#pragma once
#include"meta_stream.hpp"
#include"offset_pointer.hpp"

using namespace offset_pointer;

using namespace meta_ios;
using namespace meta_objects;


//create a meta object that converts chs to digits, supporting meta stream protocols
template<class this_int, class ch>
using make_bit = Idx<this_int::value * 10 + static_cast<std::size_t>(ch::value - '0')>;

using make_num_o = meta_object<Idx<0>, meta_quote::binary<make_bit>>;


template<class digit>
struct break_if_is_not_digit { static constexpr bool value = false; };

template<class digit> requires has_value<digit>
struct break_if_is_not_digit<digit> {
    static constexpr bool value = !(digit::value >= '0' && digit::value <= '9');
};

template<class str>
using get_num_from_str = meta_all_transfer<make_num_o, meta_istream<str>,
    //protocols::only_stream_cache_unref suggest that the transfer process will pre-read from the meta_istream,
    //that it means by 'break_if_is_not_digit', when it tries to transfer a non-digit character, it will stop the transfer process
    //before it transfer the non-digit character to the make_num_o
    protocols::only_stream_cache_unref<meta_quote::unary<break_if_is_not_digit>
    >
>;

template<unsigned int ori, char c>
struct my_elf_hash {
    static constexpr unsigned int h = (ori << 4) + static_cast<unsigned int>(c),
        g = h & 0xf0000000,
        prime_h = h ? h ^ (g >> 24) : h,
        value = h & (~g);

    template<char c>
    using input = my_elf_hash<value, c>;
};

template<class this_elf_hash, class from_ins>
using update_elf_hash = typename this_elf_hash::template input<from_ins::value>;

using elf_hash_ostream = meta_object<my_elf_hash<0, '\0'>, meta_quote::binary<update_elf_hash>>;

namespace meta_list_swap {
	//define meta_object function
	template<std::size_t from_pos, class from_type, std::size_t to_pos, class to_type> struct meta_swap_f
	{
		//while meta_stream transferring, if it encounters certain pos
		//add the specified type to typelist instead of normally transferring types from the input stream

		template<class this_list, class T> requires (exp_size<this_list> == from_pos)
			static consteval auto apply_select() {
			return typename meta_invoke<meta_ostream<this_list>, to_type>::type{};
		}
		template<class this_list, class T> requires (exp_size<this_list> == to_pos)
			static consteval auto apply_select() {
			return typename meta_invoke<meta_ostream<this_list>, from_type>::type{};
		}
		template<class this_list, class T>
		static consteval auto apply_select() {
			return typename meta_invoke<meta_ostream<this_list>, T>::type{};
		}
		template<class this_list, class T> using apply = decltype(apply_select<this_list, T>());
	};

	//define meta swap output stream
	template<class TL/*to extract certain types*/, std::size_t from, std::size_t to>
	using meta_swap_os = meta_object<
		exp_list<>,/*empty list, output container*/
		meta_swap_f<from, exp_select<from, TL>, to, exp_select<to, TL>>
	>;

	//invoking meta_stream
	template<class TL> struct meta_swap {
		template<std::size_t from, std::size_t to>
		struct swap_impl {
			using type = protocols::stream_to_t<
				meta_all_transfer<
				meta_swap_os<TL, from, to>,/*meta output stream, output to an empty list*/
				meta_istream<TL>/*meta input stream, to output stream*/
				>
			>;
		};

		template<std::size_t from, std::size_t to>
		using at = typename swap_impl<from, to>::type;
	};
}

using meta_list_swap::meta_swap;


namespace exp_utilities {
    template<class T>
    struct exp_type_base {
        constexpr exp_type_base() {};
        constexpr ~exp_type_base() {};

        template<template<class> class exp_type_base_derived, class U> requires std::is_base_of_v<exp_type_base<T>, exp_type_base_derived<T>>
        consteval bool operator==(const exp_type_base_derived<U>& another)const {
            return std::is_same_v<T, U>;
        }

        constexpr operator const char* () const {
            return typeid(T).name();
        }

        template<template<class> class F>
        consteval exp_type_base<F<T>> transform()const {
            return {};
        }
        template<class ...Args>
        T initialize(Args&& ...args)const {
            return T{ std::forward<Args>(args)... };
        }
        using type = T;
    };
}

template<class T>
struct exp_type :public exp_utilities::exp_type_base<T> {};

template<io_stream_transform_details::io_stream_traits::meta_ostream_t os>
struct exp_type<os> : public exp_utilities::exp_type_base<os>
{
    constexpr exp_type() {}
    constexpr ~exp_type() {}

    template<class T>
    consteval exp_type<meta_invoke<os, T>> operator <<(const exp_type<T>& value)const {
        return {};
    }

    template<io_stream_transform_details::io_stream_traits::meta_istream_t is>
    consteval auto operator << (const exp_type<is>& value)const -> exp_type<typename meta_all_transfer<os, is>::to>
    {
        return{};
    }

    constexpr exp_type<typename os::type> data()const {
        return {};
    }

    template<template<class...> class wrapper>
    constexpr exp_type<typename os::type::template to<wrapper>> as()const {
        return {};
    }

};

template<class TL>
using meta_ostream_type = exp_type<meta_ostream<TL>>;

template<class ...T>
using meta_istream_type = exp_type<meta_istream_list<T...>>;



struct combine {
    template<class ...Tys>
    using apply = exp_list<Tys...>;
};
template<class combiner, class ...ostreams>
struct par_ostream_o {
    using type = meta_invoke<combiner, get_type<ostreams>...>;
    template<class T>
    using apply = par_ostream_o<combiner, meta_invoke<ostreams, T>...>;
};
template<class this_par_o, class T>
using invoker = meta_invoke<this_par_o, T>;

template<class combiner, class ...ostreams>
using meta_par_ostreams = meta_object<par_ostream_o<combiner, ostreams...>, meta_quote::binary<invoker>>;

template<class TL, class F>
constexpr std::size_t meta_count_if = meta_all_transfer<meta_filter_ostream<exp_list<>, meta_fold<F, protocols::only_arg>>, meta_istream<TL>>::to::type::length;
